package com.zlyx.easy.swagger.plugins;

import static com.google.common.base.Strings.emptyToNull;
import static springfox.documentation.schema.Types.isBaseType;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.pluginDoesApply;
import static springfox.documentation.swagger.readers.parameter.Examples.examples;
import static springfox.documentation.swagger.schema.ApiModelProperties.allowableValueFromString;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.zlyx.easy.core.collections.Lists;
import com.zlyx.easy.swagger.annotations.SpringMapping;
import com.zlyx.easy.swagger.config.EasySwaggerConfiguration;

import io.swagger.annotations.ApiImplicitParam;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
public class EasySwaggerSpringMappingParametersReader implements OperationBuilderPlugin {

	private final DescriptionResolver descriptions;

	@Autowired
	public EasySwaggerSpringMappingParametersReader(DescriptionResolver descriptions) {
		this.descriptions = descriptions;
	}

	@Override
	public void apply(OperationContext context) {
		context.operationBuilder().parameters(readParameters(context));
	}

	@Override
	public boolean supports(DocumentationType delimiter) {
		return pluginDoesApply(delimiter);
	}

	private List<Parameter> readParameters(OperationContext context) {
		List<Parameter> parameters = Lists.newList();
		Optional<SpringMapping> annotation = context.findAnnotation(SpringMapping.class);
		if (annotation.isPresent()) {
			parameters = EasySwaggerConfiguration.getParameters(annotation.get().names());
			for (ApiImplicitParam param : annotation.get().apiImplicitParams()) {
				parameters.add(implicitParameter(descriptions, param));
			}
		}
		return parameters;
	}

	private ModelRef maybeGetModelRef(ApiImplicitParam param) {
		String dataType = MoreObjects.firstNonNull(emptyToNull(param.dataType()), "string");
		AllowableValues allowableValues = null;
		if (isBaseType(dataType)) {
			allowableValues = allowableValueFromString(param.allowableValues());
		}
		if (param.allowMultiple()) {
			return new ModelRef("", new ModelRef(dataType, allowableValues));
		}
		return new ModelRef(dataType, allowableValues);
	}

	public Parameter implicitParameter(DescriptionResolver descriptions, ApiImplicitParam param) {
		ModelRef modelRef = maybeGetModelRef(param);
		return new ParameterBuilder().name(param.name()).description(descriptions.resolve(param.value()))
				.defaultValue(param.defaultValue()).required(param.required()).allowMultiple(param.allowMultiple())
				.modelRef(modelRef).allowableValues(allowableValueFromString(param.allowableValues()))
				.parameterType(emptyToNull(param.paramType())).parameterAccess(param.access())
				.order(SWAGGER_PLUGIN_ORDER).scalarExample(param.example()).complexExamples(examples(param.examples()))
				.build();
	}

}
